﻿using System;
using System.Globalization;
using System.ServiceModel;
using Airborne.ScrumPoker.Model.TeamFoundation;
using Airborne.ScrumPoker.Module.Client.Model;
using Airborne.ScrumPoker.Module.Events;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Wpf.Events;

namespace Airborne.ScrumPoker.Module.Client.Controllers
{
    public class ClientController : IClientController, IDisposable
    {
        //HACK: need to be in configuration.
        private const string ServerDefinition = "net.tcp://{0}/AirborneScrumPokerService";

        #region Constructor
        public ClientController(IEventAggregator eventAggregator)
        {
            EventAggregator = eventAggregator;
        }
        #endregion

        #region Properties
        private ScrumPokerClientCallback ServerCallBack { get; set; }
        private ScrumPokerClient ScrumPokerClient { get; set; }
        private IEventAggregator EventAggregator { get; set; }
        #endregion

        #region Methods
        
        public void Run()
        {
            ServerCallBack = new ScrumPokerClientCallback();
            SubscribeToEvents();
        }

        private void SubscribeToEvents()
        {
            ServerCallBack.StartTimer += StartTimer;
            ServerCallBack.StopTimer += StopTimer;
            ServerCallBack.EndGame += EndGame;
            ServerCallBack.WorkItemSelected += WorkItemSelected;
            ServerCallBack.VoteReset += VoteReset;

            EventAggregator.GetEvent<GenericEvent<string>>().Subscribe(SubscribeClient, ThreadOption.UIThread, true, p => p.Topic == EventTopics.SubscribeClient);
            EventAggregator.GetEvent<GenericEvent<string>>().Subscribe(UnsubscribeClient, ThreadOption.BackgroundThread, true, p => p.Topic == EventTopics.UnsubscribeClient);
            EventAggregator.GetEvent<GenericEvent<double>>().Subscribe(CastVote, ThreadOption.BackgroundThread, true, p => p.Topic == EventTopics.CastVote);
            EventAggregator.GetEvent<GenericEvent<int>>().Subscribe(ApplicationShutdown, ThreadOption.BackgroundThread, true, p => p.Topic == EventTopics.ApplicationShutdown);
        }

        private void VoteReset(object sender, EventArgs e)
        {
            EventAggregator.GetEvent<GenericEvent<string>>().Publish(new EventParameters<string>(EventTopics.ResetVote, string.Empty));
        }

        private void EndGame(object sender, EventArgs e)
        {
            EventAggregator.GetEvent<GenericEvent<string>>().Publish(new EventParameters<string>(EventTopics.EndGame, string.Empty));
        }

        private void StopTimer(object sender, EventArgs e)
        {
            EventAggregator.GetEvent<GenericEvent<string>>().Publish(new EventParameters<string>(EventTopics.StopTimer, string.Empty));
        }

        private void StartTimer(object sender, GenericEventArgs<int> e)
        {
            EventAggregator.GetEvent<GenericEvent<int>>().Publish(new EventParameters<int>(EventTopics.StartTimer, e.Data));
        }

        private void WorkItemSelected(object sender, GenericEventArgs<ITeamWorkItem> e)
        {
            EventAggregator.GetEvent<GenericEvent<ITeamWorkItem>>().Publish(new EventParameters<ITeamWorkItem>(EventTopics.WorkItemSelected, e.Data));
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void SubscribeClient(EventParameters<string> parameters)
        {
            Guard.IsNotNullOrEmpty(parameters.Value, "Value");

            string developerName = parameters.Value;
            string serverName = string.Empty;

            if (parameters.Value.IndexOf('@') > 0)
            {
                string[] parts = developerName.Split(new char[] { '@' }, 2, StringSplitOptions.RemoveEmptyEntries);
                developerName = parts[0];
                serverName = string.Format(CultureInfo.InvariantCulture, ServerDefinition, parts[1]);
            }

            InstanceContext instanceContext = new InstanceContext(null, ServerCallBack);
            if (!string.IsNullOrEmpty(serverName))
            {
                NetTcpBinding binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;

                ScrumPokerClient = new ScrumPokerClient(instanceContext, binding , new EndpointAddress(serverName));
            }
            else
            {
                ScrumPokerClient = new ScrumPokerClient(instanceContext);
            }


            try
            {
                if (ScrumPokerClient.Subscribe(developerName))
                {
                    EventParameters<string> parameter = new EventParameters<string>(EventTopics.ClientSubscribed, string.Empty);
                    EventAggregator.GetEvent<GenericEvent<string>>().Publish(parameter);
                }
            }
            catch(Exception ex)
            {
                EventParameters<string> parameter = new EventParameters<string>(EventTopics.Error, ex.Message);
                EventAggregator.GetEvent<GenericEvent<string>>().Publish(parameter);
            }
 
        }

        private void UnsubscribeClient(EventParameters<string> parameters)
        {
            ScrumPokerClient.Unsubscribe();
        }

        private void ApplicationShutdown(EventParameters<int> parameters)
        {
            UnsubscribeAndClose();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void CastVote(EventParameters<double> parameters)
        {
            try
            {
                ScrumPokerClient.Vote(parameters.Value);
            }
            catch
            {
                EventParameters<string> parameter = new EventParameters<string>(EventTopics.Error, Resources.ErrorCastingVote);
                EventAggregator.GetEvent<GenericEvent<string>>().Publish(parameter);
            }
        }

        private void UnsubscribeAndClose()
        {
            if ((ScrumPokerClient != null) && (ScrumPokerClient.State == CommunicationState.Opened))
            {
                ScrumPokerClient.Unsubscribe();
                ScrumPokerClient.Close();
            }
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                ServerCallBack.StartTimer -= StartTimer;
                ServerCallBack.StopTimer -= StopTimer;
                ServerCallBack.EndGame -= EndGame;
                ServerCallBack.WorkItemSelected -= WorkItemSelected;
                ServerCallBack.VoteReset -= VoteReset;

                EventAggregator.GetEvent<GenericEvent<string>>().Unsubscribe(SubscribeClient);
                EventAggregator.GetEvent<GenericEvent<string>>().Unsubscribe(UnsubscribeClient);
                EventAggregator.GetEvent<GenericEvent<double>>().Unsubscribe(CastVote);
                EventAggregator.GetEvent<GenericEvent<int>>().Unsubscribe(ApplicationShutdown);

                UnsubscribeAndClose();
            }
        }
        #endregion

    }
}
